<?php

namespace App\Console\Commands;

use Illuminate\Support\Str;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;

class CreateModel extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:create-model
                            {--table=* : 表明}
                            {--connect= : 链接名}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '创建模型';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $all    = false;
        $configs = config('database');
        $connects = $configs['connections'] ?? [];
        // 获取传入的参数
        $table = $this->option('table');
        if (empty($table)) {
            if ($this->confirm('检测到你未传入表名, 是否要加载全部表')) {
                $all = true;
            } else {
                return $this->line("未进行任何操作", 'error');
            }
        }
        $this->info(sprintf("要生成的表: %s", $all ? '全部' : implode(',', $table)));
        $connect = $this->option('connect') ?? $configs['default'];
        $this->info(sprintf("connect: %s", $connect));
        $config = $connects[$connect] ?? $connects[$configs['default']] ?? [];
        if (empty($config)) {
            return $this->line(sprintf("未配置数据库链接: %s", $connect), 'error');
        }
        if ($config['driver'] != 'mysql') {
            return $this->line("只支持mysql类型的数据库链接", 'error');
        }
        $this->mysql($connect, $config, $table);
    }

    /**
     * @param $string
     * @param $style
     * @param $verbosity
     * @return false
     */
    public function line($string, $style = null, $verbosity = null): bool
    {
        parent::line($string, $style, $verbosity);
        return false;
    }


    /**
     * @param string $connect
     * @param string $database
     * @param array $table
     * @return void
     */
    protected function mysqlTable(string $connect, string $database, array $table): void
    {
        $sql = "SELECT COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT From information_schema.`columns` where table_schema = ? and table_name = ?";
        $columns = DB::connection($connect)->select($sql, [$database, $table['TABLE_NAME']]);
        $fillable = "";
        $properties = "";
        foreach ($columns as $column) {
            $fillable .= "'" . $column['COLUMN_NAME'] . "', ";
            $properties .= sprintf(
                ' @property %s $%s %s' . PHP_EOL . "*",
                $this->dataType2PhpType($column['DATA_TYPE']),
                $column['COLUMN_NAME'],
                $column['COLUMN_COMMENT']
            );
        }
        $con = Str::studly($connect);
        $namespace = 'App\\Models\\' . $con;
        $modelName = Str::studly($table['TABLE_NAME']);
        $tpl = file_get_contents(app()->basePath("/resources/tpl/Model.tpl"));
        $model = str_replace(
            ['{namespace}','{model}','{comment}', '{property}', '{connection}', '{tableName}', '{fillable}'],
            [$namespace, $modelName, $table['TABLE_COMMENT'], $properties, $connect, $table['TABLE_NAME'], substr($fillable, 0, strrpos($fillable, ", "))],
            $tpl
        );
        // 写入
        $this->writeModel(app()->basePath('/app/Models/'.$con.'/'.$modelName.'.php'), $model);
    }

    /**
     * @param string $connect
     * @param array $config
     * @param array $tables
     * @return void
     */
    protected function mysql(string $connect, array $config, array $tables = []): void
    {
        $sql = "SELECT TABLE_NAME,TABLE_COMMENT From information_schema.`TABLES` where table_schema = ? and TABLE_TYPE = 'BASE TABLE'";
        $result = DB::connection($connect)->select($sql, [$config['database']]);
        foreach ($result as $table) {
            if (!empty($tables) && !in_array($table['TABLE_NAME'], $tables)) {
                continue;
            }
            $this->mysqlTable($connect, $config['database'], $table);
        }
    }

    /**
     * @param string $path
     * @param string $content
     * @return void
     */
    protected function writeModel(string $path, string $content): void
    {
        $dirs = pathinfo($path, PATHINFO_DIRNAME);
        if (!is_dir($dirs)) {
            File::makeDirectory($dirs);
        }
        if (file_exists($path)) {
            $this->info(sprintf("%s 文件已存在,进行覆盖", $path));
        }
        File::replace($path, $content);
        $this->info(sprintf("%s 生成成功", $path));
    }

    /**
     * @param string $dataType
     * @return string
     */
    protected function dataType2PhpType(string $dataType): string
    {
        return match($dataType) {
            "int", "tinyint" => "int",
            "decimal" => "float",
            default => "string",
        };
    }
}
